﻿//*****************************************************************************
//
//  Copyright 2008 IT Cloud Company
//
//  This software is available under the Microsoft Public License.  Please
//  read the license agreement at http://opensource.org/licenses/ms-pl.html
//  before using the software.
//
//  Authors:
//    Don Song (don@itcloud.com)
//
//*****************************************************************************

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;

namespace HeadLinx.AD.Common
{
    public class HeadLinxPhotoCache
    {
        private string _path;

        public HeadLinxPhotoCache(string subFolderName)
        {
            Environment.SpecialFolder[] foldersToTry =
            {
                Environment.SpecialFolder.MyPictures,
                Environment.SpecialFolder.MyDocuments,
                Environment.SpecialFolder.LocalApplicationData,
            };

            String parentPath = null;

            foreach( Environment.SpecialFolder folderAlias in foldersToTry)
            {
                parentPath = Environment.GetFolderPath(folderAlias);

                if (!String.IsNullOrEmpty(parentPath))
                {
                    if (Directory.Exists(parentPath))
                    {
                        break;
                    }
                    else
                    {
                        parentPath = null;
                    }
                }
            }

            if (String.IsNullOrEmpty(parentPath))
            {
                throw new ApplicationException("Could find not your My Pictures, My Documents, or Local Application Data folder.");
            }

            _path = Path.Combine(parentPath, subFolderName);

            if (!Directory.Exists(_path))
            {
                Directory.CreateDirectory(_path);
            }
        }

        public string CacheFolder
        {
            get
            {
                return _path;
            }
        }

        public string SaveImageData(string imageName, byte[] imageData)
        {
            if (String.IsNullOrEmpty(imageName))
            {
                throw new ArgumentException("imageName parameter not specified.");
            }

            //
            //  Replace invalid filename chars in imageName.
            //
            foreach (char invalidChar in Path.GetInvalidFileNameChars())
            {
                if (imageName.IndexOf(invalidChar) != -1)
                {
                    imageName = imageName.Replace(invalidChar, '_');
                }
            }

            //
            //  Write image data to output file.
            //
            string outputFilePath = Path.Combine(_path, imageName + ".jpg"); // TODO: fix hardcoded assumption that image is jpeg

            using (FileStream fs = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
            {
                fs.Write(imageData, 0, imageData.Length);
            }

            return outputFilePath;
        }

        public byte[] GetImageData(string imageName, int maxFileAgeDays)
        {
            if (String.IsNullOrEmpty(imageName))
            {
                throw new ArgumentException("imageName parameter not specified.");
            }

            string filePath = Path.Combine(_path, imageName + ".jpg"); // TODO: fix hardcoded assumption that image is jpeg

            FileInfo fi = new FileInfo(filePath);

            if (fi.Exists &&
                ((maxFileAgeDays <= 0) ||
                (DateTime.UtcNow - fi.LastWriteTimeUtc).Days < maxFileAgeDays))
            {
                //
                //  File has not expired or caller specified "never expire"
                //  (maxFileAgeDays <= 0).
                //

                using (FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    if (fs.Length < int.MaxValue)
                    {
                        using (BinaryReader reader = new BinaryReader(fs))
                        {
                            return reader.ReadBytes((int)fs.Length);
                        }
                    }
                }
            }

            return null;
        }

        public string GetImagePath(string imageName, int maxFileAgeDays)
        {
            if (String.IsNullOrEmpty(imageName))
            {
                throw new ArgumentException("imageName parameter not specified.");
            }

            string filePath = Path.Combine(_path, imageName + ".jpg"); // TODO: fix hardcoded assumption that image is jpeg

            FileInfo fi = new FileInfo(filePath);

            if (fi.Exists &&
                ((maxFileAgeDays <= 0) ||
                (DateTime.UtcNow - fi.LastWriteTimeUtc).Days < maxFileAgeDays))
            {
                return fi.FullName;
            }

            return null;
        }

        private string ComputeUniqueNameHashString(string uniqueName)
        {
            MD5 md5 = MD5.Create();

            byte[] fullHash = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(uniqueName));

            //
            //  Truncate the full hash to 32 bits (4 bytes)... this is for the
            //  benefit of users browsing the photo cache manually, so they
            //  don't have to see unwieldly file names.
            //
            //  Obviously we are not using the MD5 hash as a security measure,
            //  we're only using it to get an immutable and unique value for a
            //  particular name.  32-bits should be unique enough for our
            //  purposes.
            //

            Debug.Assert(fullHash.Length > 4);
            byte[] hash = new byte[4];
            for (int i = 0; i < 4; ++i)
            {
                hash[i] = fullHash[i];
            }

            return Utility.ToHexBinary(hash).ToUpper();
        }
    }
}
